﻿using System;
using Microsoft.SqlServer.Dts.Runtime;
using System.IO;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using RunWrapper = Microsoft.SqlServer.Dts.Runtime.Wrapper;
using System.Collections.Generic;

namespace DatabaseGroup.IntegrationTests.SSIS.SourceAdapters
{
    public static class AutomationHelper
    {
        public static string AddFileManager(Connections connections, string connectionType, string filepath)
        {
            if (string.IsNullOrEmpty(filepath))
                throw new ArgumentNullException("filepath");

            string filename = Path.GetFileName(filepath);

            if (string.IsNullOrEmpty(filename))
                throw new Exception("Failed to determine filename from filepath");

            if (connections.Contains(filename))
            {
                int fileIndex = 1;

                while (connections.Contains(filename + " " + fileIndex))
                {
                    fileIndex++;
                }

                filename += " " + fileIndex;
            }

            ConnectionManager fileConnection = connections.Add(connectionType);
            fileConnection.Name = filename;
            fileConnection.ConnectionString = filepath;

            return filename;
        }

        public static IDTSComponentMetaData100 SetupBaseDFSComponent(Package package)
        {
            Executable pipelineTask = package.Executables.Add("STOCK:PipelineTask");
            TaskHost thMainPipe = (TaskHost)pipelineTask;
            thMainPipe.Name = "Data Flow Task";
            MainPipe dataFlowTask = (MainPipe)thMainPipe.InnerObject;

            IDTSComponentMetaData100 dfs = dataFlowTask.ComponentMetaDataCollection.New();
            dfs.Version = 1;
            dfs.Name = "DFS 2008";
            dfs.ComponentClassID = "{2E42D45B-F83C-400F-8D77-61DDE6A7DF29}";
            CManagedComponentWrapper dfsDesignTime = dfs.Instantiate();
            dfsDesignTime.SetComponentProperty("UserComponentTypeName", "DatabaseGroup.SSIS.SourceAdapters.DelimitedFileSource2008Component, DatabaseGroup.SSIS.SourceAdapters.DelimitedFileSource2008, Version=1.0.7.0, Culture=neutral, PublicKeyToken=103567041829a1c2");
            dfsDesignTime.ProvideComponentProperties();

            return dfs;
        }

        public static IDTSComponentMetaData100 AddOutputDestination(Package package, string destinationName, string destinationClassId, string fileConnection)
        {
            MainPipe dataFlowTask = GetDataFlowTaskFromPackage(package);

            IDTSComponentMetaData100 dest = dataFlowTask.ComponentMetaDataCollection.New();
            dest.Name = destinationName;
            dest.ComponentClassID = destinationClassId;

            CManagedComponentWrapper destDesignTime = dest.Instantiate();
            destDesignTime.ProvideComponentProperties();
            dest.LocaleID = 2057;

            dest.RuntimeConnectionCollection[0].ConnectionManager = DtsConvert.GetExtendedInterface(package.Connections[fileConnection]);
            dest.RuntimeConnectionCollection[0].ConnectionManagerID = package.Connections[fileConnection].ID;

            return dest;
        }

        public static IDTSPath100 LinkComponents(Package package, string sourceName, string sourceOutputName, 
            string destName, string destInputName, FlatFileConfig ffConfig)
        {
            MainPipe dataFlowTask = GetDataFlowTaskFromPackage(package);

            IDTSPath100 path = dataFlowTask.PathCollection.New();

            IDTSComponentMetaData100 source = dataFlowTask.ComponentMetaDataCollection[sourceName];
            IDTSComponentMetaData100 dest = dataFlowTask.ComponentMetaDataCollection[destName];

            IDTSOutput100 sourceOutput = source.OutputCollection[sourceOutputName];
            IDTSInput100 destInput = dest.InputCollection[destInputName];

            path.AttachPathAndPropagateNotifications(sourceOutput, destInput);

            var destDesigner = dest.Instantiate();

            destDesigner.AcquireConnections(null);
            destDesigner.ReinitializeMetaData();
            destDesigner.ReleaseConnections();

            IDTSInput100 input = dest.InputCollection[destInputName];
            IDTSVirtualInput100 vInput = input.GetVirtualInput();

            foreach (IDTSVirtualInputColumn100 vColumn in vInput.VirtualInputColumnCollection)
            {
                if (ffConfig.IgnoreColumns.Contains(vColumn.Name))
                    continue;

                var inColumn = destDesigner.SetUsageType(input.ID, vInput, vColumn.LineageID, DTSUsageType.UT_READONLY);
                destDesigner.MapInputColumn(input.ID, inColumn.ID, input.ExternalMetadataColumnCollection[vColumn.Name].ID);
            }

            return path;
        }

        public static string AddFlatFileConnection(Package package, string file, string sourceName, string sourceOutputName, FlatFileConfig flatFileConfig)
        {
            string fileManager = AddFileManager(package.Connections, "FLATFILE", file);
            ConnectionManager flatfile = package.Connections[fileManager];

            var flatFileWrapper = (RunWrapper.IDTSConnectionManagerFlatFile100)flatfile.InnerObject;
            flatFileWrapper.CodePage = flatFileConfig.CodePage;
            flatFileWrapper.ColumnNamesInFirstDataRow = flatFileConfig.ColumnNamesInFirstDataRow;
            flatFileWrapper.RowDelimiter = flatFileConfig.RowDelimiter;
            flatFileWrapper.TextQualifier = flatFileConfig.TextQualifier;
            flatFileWrapper.Unicode = flatFileConfig.Unicode;

            MainPipe dataFlowTask = GetDataFlowTaskFromPackage(package);
            IDTSComponentMetaData100 source = dataFlowTask.ComponentMetaDataCollection[0];
            IDTSOutput100 sourceOutput = source.OutputCollection[sourceOutputName];

            foreach (IDTSOutputColumn100 col in sourceOutput.OutputColumnCollection)
            {
                if (col.DataType == RunWrapper.DataType.DT_WSTR || col.DataType == RunWrapper.DataType.DT_NTEXT)
                {
                    flatFileWrapper.Unicode = true;
                    break;
                }
            }

            for (int colIndex = 0; colIndex < sourceOutput.OutputColumnCollection.Count; colIndex++)
            {
                if (flatFileConfig.IgnoreColumns.Contains(sourceOutput.OutputColumnCollection[colIndex].Name))
                    continue;

                var destCol = flatFileWrapper.Columns.Add();

                ((RunWrapper.IDTSName100)destCol).Name = sourceOutput.OutputColumnCollection[colIndex].Name;
                
                destCol.ColumnWidth = 0;
                destCol.ColumnType = "Delimited";
                destCol.MaximumWidth = sourceOutput.OutputColumnCollection[colIndex].Length;
                destCol.DataType = sourceOutput.OutputColumnCollection[colIndex].DataType;

                // flat file destination can't handle multiple codepages, so force a single one
                if (flatFileWrapper.Unicode)
                {
                    if (destCol.DataType == RunWrapper.DataType.DT_STR)
                        destCol.DataType = RunWrapper.DataType.DT_WSTR;
                    else if (destCol.DataType == RunWrapper.DataType.DT_TEXT)
                        destCol.DataType = RunWrapper.DataType.DT_NTEXT;
                }                
                
                destCol.DataPrecision = sourceOutput.OutputColumnCollection[colIndex].Precision;
                destCol.DataScale = sourceOutput.OutputColumnCollection[colIndex].Scale;
                destCol.TextQualified = flatFileConfig.IsTextQualified;

                if (colIndex + 1 == sourceOutput.OutputColumnCollection.Count)
                    destCol.ColumnDelimiter = flatFileConfig.RowDelimiter;
                else
                    destCol.ColumnDelimiter = flatFileConfig.ColumnDelimiter;
            }

            return fileManager;
        }

        public static void SavePackage(Package package, string file)
        {
            string xml;
            package.SaveToXML(out xml, null);
            File.WriteAllText(file, xml);
        }

        private static MainPipe GetDataFlowTaskFromPackage(Package package)
        {
            Executable pipelineTask = package.Executables["Data Flow Task"];
            TaskHost thMainPipe = (TaskHost)pipelineTask;
            return (MainPipe)thMainPipe.InnerObject;
        }
    }
}
